<?php
namespace bdhert\PhpBitfield;

use bdhert\PhpBitfield\exception\InformatsException;
use bdhert\PhpBitfield\exception\StructException;

final class BitCollect extends BitANLS implements \Countable,Collect,BitString {
    /**
     * 获取结果列表
     * @return array|mixed
     */
    public function get(): array {
        $data = [];
        $this->mapActions(static function ($index, $fields) use(&$data) {
            $data[] = array_merge(['index' => $index], $fields);
        }, true, true);

        return $data;
    }

    /**
     * 结果统计
     * @return int
     */
    public function count(): int {
        if (empty($this->conditions)) return $this->head->total;

        return count($this->get());
    }

    /**
     * 获取首值
     * @return array|null
     */
    public function first(): ?array {
        $data = $this->limit(1, 1)->get();
        return $data[0] ?? NULL;
    }

    /************ 位图操作 **************/

    /**
     * 获取位图位状态
     * @param int $index
     * @return int|null
     */
    public function getbit(int $index): ?int {
        if (!$this->isBitmap()) throw new StructException('非位图结构', 400);

        return $this->getValue($index, 0, true);
    }

    /**
     * 位图置位
     * @param int $index
     * @return bool|mixed|string
     */
    public function setbit(int $index) {
        if (is_null($bit = $this->getbit($index))) return false;

        if (!$bit) {
            $this->setValue($index, 0, 1, true);
            $this->replace(95, 10, ++$this->head->bitcount);
        }

        $this->initialize($string = $this->string());
        return $string;
    }

    /**
     * 位图删除
     * @param int $index
     * @return bool|mixed|string
     */
    public function delbit(int $index) {
        if (is_null($bit = $this->getbit($index))) return false;

        if ($bit) {
            $this->setValue($index, 0, 0, true);
            if ($this->head->bitcount) $this->replace(95, 10, --$this->head->bitcount);
        }

        $this->initialize($string = $this->string());
        return $string;
    }

    /**
     * 位统计
     * @return int
     */
    public function bitcount(): int {
        if (!$this->isBitmap()) throw new StructException('非位图结构', 400);
        if (empty($this->conditions)) return $this->head->bitcount;

        $bitcount = 0;
        $this->mapActions(static function ($index, $fields) use(&$bitcount) {
            $fields[0] && $bitcount++;
        }, true, true);

        return $bitcount;
    }

    /************ 位域修改 **************/

    /**
     * 自增
     * @param int $n
     * @param int $number
     * @return mixed|string
     */
    public function increment(int $n, int $number = 1) {
        $self = &$this;
        return $this->update([$n => static function (int $m, int $n) use ($number, &$self) {
            $pre_v = $self->getValue($m, $n, true);
            return $pre_v + $number;
        }]);
    }

    /**
     * 自减
     * @param int $n
     * @param int $number
     * @return mixed|string
     */
    public function decrement(int $n, int $number = 1) {
        $self = &$this;
        return $this->update([$n => static function (int $m, int $n) use ($number, &$self) {
            $pre_v = $self->getValue($m, $n, true);

            if (($value = $pre_v - $number) < 0) throw new InformatsException('更新值超出最低范围', 400);

            return $value;
        }]);
    }

    /**
     * 更新数据
     * @param array $saves
     * @return mixed|string
     */
    public function update(array $saves) {
        if ($this->isDimensions($saves)) throw new InformatsException('保存格式错误', 400);

        $self = &$this;
        $this->mapActions(static function ($index, $fields) use($saves, &$self) {
            foreach ($saves as $save_index => $save) {
                $save = BitRigger::valueFormat(($save instanceof \Closure) ? $save($index, $save_index) : $save);

                if (
                    $self->isBitmap($save_index = (int)$save_index) && !($method = ['delbit', 'setbit'][$save] ?? NULL)
                ) {
                    $self->{$method}($index);
                } else {
                    $self->setValue($index, $save_index, $save, true);
                }
            }
        }, true);

        $this->initialize($string = $this->string());
        return $string;
    }

    /**
     * 插入数据
     * @param array $save
     * @return mixed|string
     */
    public function insert(array $save) {
        $saves = $this->isDimensions($save) ? $save : [$save];

        foreach ($saves as $info) {
            $this->addValue($info);
        }

        $this->initialize($string = $this->string());
        return $string;
    }

    /************ 操作条件 **************/

    /**
     * 分页条件
     * @param int $page
     * @param int $limit
     * @return $this|Collect
     */
    public function limit(int $page, int $limit = 0): Collect {
        empty($limit) && $limit = $this->limit;
        if ($page < 1 || $limit < 1) throw new InformatsException('分页参数错误', 400);

        [$this->page, $this->limit] = [$page, $limit];

        return $this;
    }

    /**
     * 构造条件
     * @param array $conditions
     * @param null $operation
     * @param null $value
     * @return $this|Collect
     */
    public function where($conditions = [], $operation = NULL, $value = NULL): Collect {
        // 标准条件构造
        if (!is_array($conditions)) {
            if (is_null($operation)) throw new InformatsException('条件格式错误', 400);

            if (is_null($value)) return $this->whereStrict($conditions, '=', $operation);

            return $this->whereStrict(...func_get_args());
        }

        if (!$this->isDimensions($conditions)) $conditions = [$conditions];

        foreach ($conditions as $condition) {
            if (3 !== count($condition)) throw new InformatsException('条件格式错误', 400);

            $this->whereStrict(...$condition);
        }

        return $this;
    }

    /**
     * 充值查询构造
     * @param array $conditions
     * @param null $operation
     * @param null $value
     * @return Collect
     */
    public function whereReset($conditions = [], $operation = NULL, $value = NULL): Collect {
        $this->reset(false);

        return $this->where($conditions, $operation, $value);
    }

    /**
     * 严格条件构造
     * @param $index
     * @param $operation
     * @param $value
     * @return $this|Collect
     */
    public function whereStrict($index, $operation, $value): Collect {
        !isset($this->conditions[$index = is_numeric($index) ? (int)$index : $index]) && $this->conditions[$index] = [];

        $this->conditions[$index][] = [$operation, $value];

        return $this;
    }

    /**
     * 倒序
     * @return $this|Collect
     */
    public function desc(): Collect {
        $this->sort = SORT_DESC;
        return $this;
    }

    /**
     * 顺序
     * @return $this|Collect
     */
    public function asc(): Collect {
        $this->sort = SORT_ASC;
        return $this;
    }

    /**
     * 类型检测
     * @return bool
     */
    public function formatCheck(): bool {
        return 1 === ($this->head->field->fields[0] ?? 0) && $this->head->field->total > 1;
    }
}